package com.zho.common.fileserver;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.net.URL;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import org.apache.commons.vfs.CacheStrategy;
import org.apache.commons.vfs.FileContent;
import org.apache.commons.vfs.FileObject;
import org.apache.commons.vfs.FileSystemManager;
import org.apache.commons.vfs.FileSystemOptions;
import org.apache.commons.vfs.FileType;
import org.apache.commons.vfs.Selectors;
import org.apache.commons.vfs.auth.StaticUserAuthenticator;
import org.apache.commons.vfs.cache.SoftRefFilesCache;
import org.apache.commons.vfs.impl.DefaultFileSystemConfigBuilder;
import org.apache.commons.vfs.impl.StandardFileSystemManager;
import org.apache.log4j.Logger;

public class FilesAdapterImpl implements FilesAdapter {
    private static Logger logger = Logger.getLogger(FilesAdapterImpl.class);

    private FileSystemManager m_fileMgr = null;
    private FileSystemOptions opts = new FileSystemOptions();

    private static final int COPY = 1;

    private static final int CHANGE = 2;

    private static final int NO_COPY = 0;

    private static final String UNICODE_ENCODE = "utf-8";

    /**
     * init FileSystemManager
     * 
     * @throws Exception
     */
    public FilesAdapterImpl() {
        this.init();
    }

    /**
     * 
     */
    private void init() {
        final String LOCATION = "int()";
        try {
            if(logger.isDebugEnabled())
                logger.debug(LOCATION + ":: BEGIN");
            if (m_fileMgr == null) {
                try {                    
                    StandardFileSystemManager stdMgr = new StandardFileSystemManager();
                    stdMgr.setFilesCache(new SoftRefFilesCache());
                    stdMgr.setCacheStrategy(CacheStrategy.ON_RESOLVE);
                    stdMgr.init();
                    m_fileMgr = stdMgr;                                       
                } catch (Exception fe) {
                    logger.info(fe.getMessage());
                    throw new Exception(LOCATION, fe);
                }
            }
            if(logger.isDebugEnabled())
                logger.debug(LOCATION + ":: END");
        } catch (Exception sysex) {
            logger.info(sysex.getMessage());
        }        
    }
    
    /**
     * 
     */
    public void reInitial() {
        this.disconnect();
        this.init();
    }
    
    /**
     * 
     */
    public void disconnect() {
        final String LOCATION = "disconnect()";
        if(logger.isDebugEnabled())
            logger.debug(LOCATION + ":: BEGIN");
        if (m_fileMgr != null && m_fileMgr instanceof StandardFileSystemManager) {            
            ((StandardFileSystemManager) m_fileMgr).freeUnusedResources();
            ((StandardFileSystemManager) m_fileMgr).close();
            m_fileMgr = null;
        }
        if(logger.isDebugEnabled())
            logger.debug(LOCATION + ":: END");
    }

    /**
     * Check connections.
     * 
     * @param strConnection
     */
    public void freeUnusedResources() {
        if (m_fileMgr != null && m_fileMgr instanceof StandardFileSystemManager) {
            ((StandardFileSystemManager) m_fileMgr).freeUnusedResources();
        }
    }

    /**
     * 
     * @param username
     * @param password
     * @return
     */
    public boolean setUserAuth(String username, String password) {
        final String LOCATION = "setUserAuth()";
        try {
            StaticUserAuthenticator auth = new StaticUserAuthenticator(null, username, password);
            DefaultFileSystemConfigBuilder.getInstance().setUserAuthenticator(opts, auth);
            return true;
        } catch (Exception e) {
            logger.warn(LOCATION, e);
        }
        return false;
    }

    // ===============================================================================================
    /**
     * close FileObject
     * 
     * @param objFile
     * @param LOCATION
     */
    private void close(FileObject objFile, String LOCATION) {
        try {
            if (objFile != null) {
                objFile.close();
                objFile = null;
            }
        } catch (Exception fse) {
            logger.debug(LOCATION + " Close file object exception: " + fse.getMessage());
        }
    }

    /**
     * @param objContent
     * @param LOCATION
     */
    private void close(FileContent objContent, String LOCATION) {

        try {
            if (objContent != null) {
                objContent.close();
                objContent = null;
            }
        } catch (Exception fse) {
            logger.debug(LOCATION + " Close file object exception: " + fse.getMessage());
        }
    }

    /**
     * @param lstFileObject
     * @param LOCATION
     */
    private void close(FileObject[] lstFileObject, String LOCATION) {

        close(lstFileObject, null, LOCATION);
    }

    /**
     * @param lstFileObject
     * @param excludeObj
     * @param LOCATION
     */
    private void close(FileObject[] lstFileObject, FileObject excludeObj, String LOCATION) {

        int i, size = (lstFileObject == null ? 0 : lstFileObject.length);
        for (i = 0; i < size; i++) {
            if (!(excludeObj != null && excludeObj == lstFileObject[i])) {
                close(lstFileObject[i], LOCATION);
            }
        }
    }

    /**
     * @param lstObject
     * @param excludeObj
     * @param LOCATION
     */
    private void close(Collection<FileObject> lstObject, FileObject excludeObj, String LOCATION) {
        Iterator<FileObject> iter = (lstObject == null ? null : lstObject.iterator());
        while (iter != null && iter.hasNext()) {
            close(iter.next(), LOCATION);
        }
    }

    /**
     * @param out
     * @param LOCATION
     */
    private void close(OutputStream out, String LOCATION) {
        try {
            if (out != null) {
                out.close();
                out = null;
            }
        } catch (IOException ioex) {
            logger.debug(LOCATION + " Close file object exception: " + ioex.getMessage());
        }
    }

    /**
     * @param out
     * @param LOCATION
     */
    private void close(OutputStreamWriter out, String LOCATION) {
        try {
            if (out != null) {
                out.close();
                out = null;
            }
        } catch (IOException ioex) {
            logger.debug(LOCATION + " Close file object exception: " + ioex.getMessage());
        }
    }

    /**
     * @param out
     * @param LOCATION
     */
    private void close(BufferedWriter out, String LOCATION) {
        try {
            if (out != null) {
                out.close();
                out = null;
            }
        } catch (IOException ioex) {
            logger.debug(LOCATION + " Close file object exception: " + ioex.getMessage());
        }
    }

    /**
     * @param in
     * @param LOCATION
     */
    private void close(InputStream in, String LOCATION) {
        try {
            if (in != null) {
                in.close();
                in = null;
            }
        } catch (IOException ioex) {
            logger.debug(LOCATION + " Close file object exception: " + ioex.getMessage());
        }
    }

    /**
     * @param in
     * @param LOCATION
     */
    private void close(BufferedReader in, String LOCATION) {
        try {
            if (in != null) {
                in.close();
                in = null;
            }
        } catch (IOException ioex) {
            logger.debug(LOCATION + " Close file object exception: " + ioex.getMessage());
        }
    }

    /**
     * @param in
     * @param LOCATION
     */
    private void close(InputStreamReader in, String LOCATION) {

        try {
            if (in != null) {
                in.close();
                in = null;
            }
        } catch (IOException ioex) {
            logger.debug(LOCATION + " Close file object exception: " + ioex.getMessage());
        }
    }

    // ===============================================================================================

    /**
     * @param srcPath
     * @param dstPath
     * @return
     */
    public void copyFile(String srcPath, String dstPath) throws Exception {
        final String LOCATION = "::copy(srcPath=" + srcPath + ", dstPath=" + dstPath + ")";
        // logger.debug( LOCATION + "::BEGIN");

        FileObject src = null;
        FileObject dest = null;
        try {
            src = m_fileMgr.resolveFile(srcPath, opts);
            dest = m_fileMgr.resolveFile(dstPath, opts);
            if (src.exists() && src.getType() == FileType.FILE) {
            	if(src.getContent().getSize() > 0) {
            		dest.copyFrom(src, Selectors.SELECT_SELF);
            	} else {
            		//create empty file
            		dest.createFile();
            	}
            } else {
                throw new Exception(LOCATION + ":: Can't copy file!");
            }
        } catch (Exception fex) {
            throw new Exception(LOCATION, fex);
        } finally {
            // close FileObject Destination
            close(dest, LOCATION);
            // close FileObject Source
            close(src, LOCATION);
        }
    }

    /**
     * @param pathSrc
     * @param pathDesc
     * @param isOverWrite
     * @return
     * @throws Exception
     */
    public Map<String, String> copyFiles(String pathSrc, String pathDesc, boolean isOverWrite) throws Exception {
        final String LOCATION = "::copyFiles( [ pathSrc = " + pathSrc + ", pathDesc = " + pathDesc + ", isOverWrite = " + isOverWrite + " ] )";

        FileObject objSrc = null;
        FileObject objDest = null;
        try {
            objSrc = m_fileMgr.resolveFile(pathSrc, opts);
            objDest = m_fileMgr.resolveFile(pathDesc, opts);
            String baseURL = objDest.getName().getPath();
            baseURL = baseURL.substring(0, baseURL.lastIndexOf(objDest.getName().getBaseName()));
            return copyFiles(objSrc, objDest, isOverWrite, baseURL);
        } catch (Exception fse) {
            throw new Exception(LOCATION, fse);
        } finally {
            close(objSrc, LOCATION);
            close(objDest, LOCATION);
        }
    }

    /**
     * @param objSrc
     * @param objDesc
     * @param isOverWrite
     * @return
     * @throws Exception
     */
    private Map<String, String> copyFiles(FileObject objSrc, FileObject objDesc, boolean isOverWrite, String baseURL) throws Exception {
        final String LOCATION = "::copyFiles(objSrc=" + (objSrc == null ? "NULL" : objSrc.getName().getURI()) + ", objDesc=" + (objDesc == null ? "NULL" : objDesc.getName().getURI()) + ")";
        logger.debug(LOCATION + "::BEGIN");

        Map<String, String> mapNames = new HashMap<String, String>();

        FileObject tmp = null;
        FileObject objTmp = null;
        FileObject[] lstFileObject = null;
        try {
            if (objSrc == null || (objSrc.exists() && objSrc.getType() == FileType.FILE) || objDesc == null || (objDesc.exists() && objDesc.getType() == FileType.FILE)) {
                return mapNames;
            }

            lstFileObject = objSrc.getChildren();

            String str;
            String strName;
            int i, size = lstFileObject == null ? 0 : lstFileObject.length;
            for (i = 0; i < size; i++) {
                objTmp = lstFileObject[i];

                if (objTmp.getType() == FileType.FOLDER) {
                    tmp = objDesc.resolveFile(objTmp.getName().getBaseName());
                    mapNames.putAll(copyFiles(objTmp, tmp, isOverWrite, baseURL));
                } else if (isOverWrite) {
                    tmp = objDesc.resolveFile(objTmp.getName().getBaseName());
                    tmp.copyFrom(objTmp, Selectors.SELECT_SELF);
                    try {
                        strName = objTmp.getName().getPath().substring(baseURL == null ? 0 : baseURL.length());
                    } catch (Exception e) {
                        strName = "";
                    }
                    mapNames.put(strName, tmp.getName().getPath());
                } else {
                    str = objTmp.getName().getBaseName();
                    tmp = objDesc.resolveFile(str);
                    if (tmp.exists()) {
                        str = String.valueOf(System.currentTimeMillis()) + objTmp.getName().getBaseName();
                        tmp = objDesc.resolveFile(str);
                    }
                    tmp.copyFrom(objTmp, Selectors.SELECT_SELF);
                    try {
                        strName = objTmp.getName().getPath().substring(baseURL == null ? 0 : baseURL.length());
                    } catch (Exception e) {
                        strName = "";
                    }
                    mapNames.put(strName, objTmp.getName().getPath());
                }

                // close FileObject Destination
                close(objTmp, LOCATION);
                // close FileObject Tmp
                close(tmp, LOCATION);
            }
            logger.debug(LOCATION + "::END");
            return mapNames;
        } catch (Exception fe) {
            throw new Exception(fe);
        } finally {
            // close FileObject Source
            close(objSrc, LOCATION);

            // close FileObject Destination
            close(objDesc, LOCATION);

            // close FileObject Tmp
            close(tmp, LOCATION);

            // close FileObject ObjectTmp
            close(objTmp, LOCATION);

            // close lstFileObject
            close(lstFileObject, LOCATION);
        }
    }

    /**
     * @param pathSrc
     * @param pathDesc
     * @param isOverWrite
     * @return
     * @throws Exception
     */
    public Map<String, String> copy(String pathSrc, String pathDesc, boolean isOverWrite) throws Exception {
        return copy(pathSrc, pathDesc, isOverWrite, null);
    }

    /**
     * @param pathSrc
     * @param pathDesc
     * @return Map object that store all files which was changed name.
     */
    public Map<String, String> copy(String pathSrc, String pathDesc, boolean isOverWrite, String[] excludeFiles) throws Exception {
        final String LOCATION = "::copy( [ pathSrc = " + pathSrc + ", pathDesc = " + pathDesc + ", isOverWrite = " + isOverWrite + " ] )";

        FileObject objSrc = null;
        FileObject objDest = null;
        try {
            objSrc = m_fileMgr.resolveFile(pathSrc, opts);
            objDest = m_fileMgr.resolveFile(pathDesc, opts);
            return copy(objSrc, objDest, isOverWrite, convertToString(excludeFiles));
        } catch (Exception fse) {
            throw new Exception(LOCATION, fse);
        } finally {
            close(objSrc, LOCATION);
            close(objDest, LOCATION);
        }
    }

    /**
     * @param objSrc
     * @param objDesc
     * @param isOverWrite
     * @param excludeFiles
     * @return
     * @throws Exception
     */
    private Map<String, String> copy(FileObject objSrc, FileObject objDesc, boolean isOverWrite, String strExcludeFiles) throws Exception {
        final String LOCATION = "::copy(objSrc=" + (objSrc == null ? "NULL" : objSrc.getName().getURI()) + ", objDesc=" + (objDesc == null ? "NULL" : objDesc.getName().getURI()) + ")";
        logger.debug(LOCATION + "::BEGIN");
        Map<String, String> mapNames = new HashMap<String, String>();
        FileObject tmp = null;
        FileObject objTmp = null;
        FileObject[] lstFileObject = null;
        try {
            if (objSrc == null || (objSrc.exists() && objSrc.getType() == FileType.FILE) || objDesc == null || (objDesc.exists() && objDesc.getType() == FileType.FILE)) {
                return mapNames;
            }

            lstFileObject = objSrc.getChildren();

            String str;
            int i, size = lstFileObject == null ? 0 : lstFileObject.length;
            for (i = 0; i < size; i++) {
                objTmp = lstFileObject[i];

                if (!isExistOnListFile(objTmp, strExcludeFiles)) {
                    if (objTmp.getType() == FileType.FOLDER) {
                        tmp = objDesc.resolveFile(objTmp.getName().getBaseName());
                        mapNames.putAll(copy(objTmp, tmp, isOverWrite, strExcludeFiles));
                    } else if (isOverWrite) {
                        tmp = objDesc.resolveFile(objTmp.getName().getBaseName());
                        tmp.copyFrom(objTmp, Selectors.SELECT_SELF);
                    } else {
                        switch (check(objTmp, objDesc)) {
                        case COPY:
                            tmp = objDesc.resolveFile(objTmp.getName().getBaseName());
                            tmp.copyFrom(objTmp, Selectors.SELECT_SELF);
                            break;
                        case CHANGE:
                            str = String.valueOf(System.currentTimeMillis()) + objTmp.getName().getBaseName();
                            tmp = objDesc.resolveFile(str);
                            tmp.copyFrom(objTmp, Selectors.SELECT_SELF);
                            // add files change
                            mapNames.put(objTmp.getName().getPath(), str);
                            break;
                        case NO_COPY:
                            break;
                        default:
                            break;
                        }
                    }
                }// if (!isExcludeFile(objTmp, excludeFiles))
                else {
                    // nothing
                }
                // close FileObject Destination
                close(objTmp, LOCATION);

                // close FileObject Tmp
                close(tmp, LOCATION);
            }
            logger.debug(LOCATION + "::END");

            return mapNames;
        } catch (Exception fe) {
            throw new Exception(LOCATION, fe);
        } finally {
            // close FileObject Source
            close(objSrc, LOCATION);

            // close FileObject Destination
            close(objDesc, LOCATION);

            // close FileObject Destination
            close(objTmp, LOCATION);

            // close FileObject Tmp
            close(tmp, LOCATION);

            // close lstFileObject
            close(lstFileObject, LOCATION);
        }
    }

    /**
     * @param filePath
     * @param content
     * @return String
     * @throws Exception
     */
    public String createFile(String filePath, String content) throws Exception {
        final String LOCATION = "::createFile(" + filePath + ", Content=" + (content == null ? "NULL" : "NOT NULL") + ")";
        BufferedWriter out = null;
        FileObject fileObj = null;
        FileContent fileContent = null;
        OutputStream outTmp1 = null;
        OutputStreamWriter outTmp2 = null;

        logger.debug(LOCATION + "::BEGIN");
        try {
            fileObj = m_fileMgr.resolveFile(filePath, opts);
            if (!fileObj.exists()) {
                fileObj.createFile();
            }
            fileContent = fileObj.getContent();
            outTmp1 = fileContent.getOutputStream();
            outTmp2 = new OutputStreamWriter(outTmp1, UNICODE_ENCODE);
            out = new BufferedWriter(outTmp2);
            out.write(content);
            out.flush();
            logger.debug(LOCATION + "::END");
            return fileObj.getName().getURI();
        } catch (Exception e) {
            throw new Exception(LOCATION, e);
        } finally {
            close(outTmp1, LOCATION);

            close(outTmp2, LOCATION);

            close(out, LOCATION);

            close(fileContent, LOCATION);

            close(fileObj, LOCATION);
        }
    }

    /**
     * @param filePath
     * @param content
     * @return String
     * @throws Exception
     */
    public String createFile(String filePath, byte[] content) throws Exception {
        final String LOCATION = "::createFile(" + filePath + ", Content=" + (content == null ? "NULL" : "NOT NULL") + ")";
        OutputStream out = null;
        FileObject fileObj = null;
        FileContent fileContent = null;
        // logger.debug( LOCATION + "::BEGIN");
        try {
            fileObj = m_fileMgr.resolveFile(filePath, opts);

            if (fileObj.exists() && fileObj.getType() == FileType.FOLDER) {
                throw new Exception(LOCATION + "it was folder!");
            }

            if (!fileObj.exists()) {
                fileObj.createFile();
            }
            fileContent = fileObj.getContent();
            out = fileContent.getOutputStream();
            out.write(content);
            out.flush();
            // logger.debug( LOCATION + "::END");
            return fileObj.getName().getURI();
        } catch (Exception e) {
            throw new Exception(LOCATION, e);
        } finally {
            close(out, LOCATION);
            close(fileContent, LOCATION);
            close(fileObj, LOCATION);
        }

    }

    /**
     * @param filePath
     * @param content
     * @return String - path + file (path loads from XML file)
     * @throws Exception
     */
    public String createFile(String filePath, char[] content) throws Exception {
        final String LOCATION = "::createFile(" + filePath + ", Content=" + (content == null ? "NULL" : "NOT NULL") + ")";
        try {
            return createFile(filePath, new String(content));
        } catch (Exception se) {
            throw new Exception(LOCATION, se);
        }
    }

    /**
     * @param filePath
     * @param content
     * @return String - path + file (path loads from XML file)
     * @throws Exception
     */
    public String createFile(String filePath, InputStream input) throws Exception {
        final String LOCATION = "::createFile(" + filePath + ", InputStream)";
        // logger.debug( LOCATION + "::BEGIN");
        String strReturn = null;
        OutputStream out = null;
        FileObject fileObj = null;
        FileContent fileContent = null;
        try {
            if (input != null) {
                fileObj = m_fileMgr.resolveFile(filePath, opts);

                if (fileObj.exists() && fileObj.getType() == FileType.FOLDER) {
                    throw new Exception(LOCATION + "it was folder!");
                }
                if (!fileObj.exists()) {
                    fileObj.createFile();
                }

                // logger.debug( LOCATION + "::Get FileContent");
                fileContent = fileObj.getContent();

                out = fileContent.getOutputStream();
                byte[] tmp = new byte[2048];
                int i = -1;
                while ((i = input.read(tmp, 0, 2048)) != -1) {
                    out.write(tmp, 0, i);
                }
                out.flush();

                // logger.debug( LOCATION + "::END");
                strReturn = fileObj.getName().getURI();
            }
            return strReturn;
        } catch (Exception fse) {
            // logger.debug( fse.toString());
            throw new Exception(LOCATION, fse);
        } finally {
            close(out, LOCATION);
            close(fileContent, LOCATION);
            close(fileObj, LOCATION);
            close(input, LOCATION);
        }
    }

    /**
     * @param folderPath
     * @param onSubFolder
     * @return
     * @throws Exception
     */
    public List<String> listFiles(String folderPath, boolean onSubFolder) throws Exception {
        return listFiles(folderPath, null, onSubFolder);
    }

    /**
     * @param folderPath
     * @param filesType
     * @param onSubFolder
     * @return
     * @throws Exception
     */
    public List<String> listFiles(String folderPath, String[] filesType, boolean onSubFolder) throws Exception {

        final String PARAM = "folderPath=" + folderPath + ", filesType=" + (filesType == null ? 0 : filesType.length) + ", onSubFolder=" + onSubFolder;
        final String LOCATION = "::listFiles(" + PARAM + ")";

        List<String> listFiles = new ArrayList<String>();
        filesType = (filesType == null ? new String[0] : filesType);
        Arrays.sort(filesType);

        FileObject folder = null;

        // logger.debug( LOCATION + "::BEGIN");
        try {
            folder = m_fileMgr.resolveFile(folderPath, opts);
            if (folder.exists() && folder.getType() == FileType.FOLDER) {
                int size = filesType == null ? 0 : filesType.length;
                String strFilesType = null;
                if (size > 0) {
                    for (int i = 0; i < size; i++) {
                        strFilesType += "." + filesType[i] + ";";
                    }
                }
                String folderTmp = folder.getName().getURI();
                listFiles.addAll(listFiles(folderTmp, folderTmp, strFilesType, onSubFolder));
            }
            // logger.debug( LOCATION + "::END -- return=" +
            // listFiles.size());
            return listFiles;
        } catch (Exception e) {
            throw new Exception(LOCATION, e);
        } finally {
            // close folder FileObject
            close(folder, LOCATION);
        }
    }

    /**
     * 
     */
    public List<String> listFolders(String folderPath, boolean onSubFolder) throws Exception {
        final String PARAM = "folderPath=" + folderPath + ", onSubFolder=" + onSubFolder;
        final String LOCATION = "::listFiles(" + PARAM + ")";

        List<String> listFiles = new ArrayList<String>();

        FileObject folder = null;

        // logger.debug( LOCATION + "::BEGIN");
        try {
            folder = m_fileMgr.resolveFile(folderPath, opts);
            if (folder.exists() && folder.getType() == FileType.FOLDER) {
                String folderTmp = folder.getName().getURI();
                listFiles.addAll(listFolders(folderTmp, folderTmp, onSubFolder));
            }
            // logger.debug( LOCATION + "::END -- return=" +
            // listFiles.size());
            return listFiles;
        } catch (Exception e) {
            throw new Exception(LOCATION, e);
        } finally {
            // close folder FileObject
            close(folder, LOCATION);
        }
    }

    /**
     * @param folderPath
     * @param baseFolderPath
     * @param onSubFolder
     * @return
     * @throws Exception
     */
    private List<String> listFolders(String folderPath, String baseFolderPath, boolean onSubFolder) throws Exception {
        final String PARAM = "folderPath=" + folderPath + ", baseFolderPath=" + baseFolderPath + ", onSubFolder=" + onSubFolder;
        final String LOCATION = "::listFiles(" + PARAM + ")";

        List<String> listFiles = new ArrayList<String>();
        baseFolderPath = (baseFolderPath == null ? "" : baseFolderPath);

        FileObject folder = null;
        FileObject[] fileObjList = null;
        FileObject objTmp = null;
        // logger.debug( LOCATION + "::BEGIN");
        try {
            folder = m_fileMgr.resolveFile(folderPath, opts);
            if (folder.exists() && folder.getType() == FileType.FOLDER) {
                fileObjList = folder.getChildren();

                String filePath;
                int i, size = fileObjList.length;
                for (i = 0; i < size; i++) {
                    objTmp = fileObjList[i];
                    if (objTmp.getType() == FileType.FOLDER) {
                        filePath = objTmp.getName().getURI();
                        filePath = (filePath.startsWith(baseFolderPath) ? filePath.substring(baseFolderPath.length()) : filePath);
                        listFiles.add(filePath);
                        if (onSubFolder) {
                            // find in subFolder
                            listFiles.addAll(listFolders(objTmp.getName().getURI(), baseFolderPath, onSubFolder));
                        }
                    }
                    // close file & folder FilesObject
                    close(objTmp, LOCATION);

                } // end for
            }
            // logger.debug( LOCATION + "::END -- return=" +
            // listFiles.size());
            return listFiles;
        } catch (Exception e) {
            throw new Exception(LOCATION, e);
        } finally {
            // close folder FileObject
            close(folder, LOCATION);

            close(objTmp, LOCATION);

            close(fileObjList, LOCATION);
        }
    }

    /**
     * @param folderPath
     * @param baseFolderPath
     * @param filesType
     * @param onSubFolder
     * @return
     * @throws Exception
     */
    private List<String> listFiles(String folderPath, String baseFolderPath, String strFilesType, boolean onSubFolder) throws Exception {
        final String PARAM = "folderPath=" + folderPath + ", baseFolderPath=" + baseFolderPath + ", strFilesType=" + strFilesType + ", onSubFolder=" + onSubFolder;
        final String LOCATION = "::listFiles(" + PARAM + ")";

        List<String> listFiles = new ArrayList<String>();
        baseFolderPath = (baseFolderPath == null ? "" : baseFolderPath);

        FileObject folder = null;
        FileObject[] fileObjList = null;
        FileObject objTmp = null;

        // logger.debug( LOCATION + "::BEGIN");
        try {
            folder = m_fileMgr.resolveFile(folderPath, opts);
            if (folder.exists() && folder.getType() == FileType.FOLDER) {
                fileObjList = folder.getChildren();
                int i, size = fileObjList.length;
                String filePath = null;
                String extFiles;
                for (i = 0; i < size; i++) {
                    objTmp = fileObjList[i];
                    if (objTmp.getType() == FileType.FOLDER) {
                        if (onSubFolder) {
                            // find in subFolder
                            listFiles.addAll(listFiles(objTmp.getName().getURI(), baseFolderPath, strFilesType, onSubFolder));
                        }
                    } else {
                        extFiles = "." + objTmp.getName().getExtension() + ";";
                        if (strFilesType == null || strFilesType.indexOf(extFiles) != -1) {
                            filePath = objTmp.getName().getURI();
                            filePath = (filePath.startsWith(baseFolderPath) ? filePath.substring(baseFolderPath.length() + 1) : filePath);
                            listFiles.add(filePath);
                        }
                    }
                    // close file & folder FilesObject
                    close(objTmp, LOCATION);
                }
            }
            // logger.debug( LOCATION + "::END -- return=" +
            // listFiles.size());
            return listFiles;
        } catch (Exception e) {
            throw new Exception(LOCATION, e);
        } finally {
            // close folder FileObject
            close(folder, LOCATION);
            close(objTmp, LOCATION);
            close(fileObjList, LOCATION);
        }
    }

    /**
     * @param pathSrc
     * @throws Exception
     */
    public void delete(String pathSrc) throws Exception {

        final String LOCATION = "::delete(" + pathSrc + ")";

        // logger.debug( LOCATION + "::BEGIN");
        FileObject fileObj = null;
        try {
            fileObj = m_fileMgr.resolveFile(pathSrc, opts);
            fileObj.delete(Selectors.SELECT_ALL);
            // logger.debug( LOCATION + "::END");
        } catch (Exception fe) {
            throw new Exception(LOCATION, fe);
        } finally {
            // close folder FileObject
            close(fileObj, LOCATION);
        }
    }

    /**
     * @param pathSrc
     * @throws Exception
     */
    public void deleteFile(String pathSrc) throws Exception {

        final String LOCATION = "::deleteFile(" + pathSrc + ")";

        // logger.debug( LOCATION + "::BEGIN");
        FileObject fileObj = null;
        try {
            fileObj = m_fileMgr.resolveFile(pathSrc, opts);
            if (fileObj.exists() && fileObj.getType() == FileType.FILE) {
                fileObj.delete(Selectors.SELECT_SELF);
            } else {
                throw new Exception(LOCATION + "Can't delete file!");
            }
            // logger.debug( LOCATION + "::END");
        } catch (Exception syse) {
            throw syse;
        } finally {
            // close folder FileObject
            close(fileObj, LOCATION);
        }
    }

    /**
     * @param pathSrc
     * @throws Exception
     */
    public void deleteFolder(String pathSrc) throws Exception {
        final String LOCATION = "::deleteFolder(" + pathSrc + ")";
        // logger.debug( LOCATION + "::BEGIN");
        FileObject fileObj = null;
        try {
            fileObj = m_fileMgr.resolveFile(pathSrc, opts);
            if (fileObj.exists() && fileObj.getType() == FileType.FOLDER) {
                fileObj.delete(Selectors.SELECT_ALL);
            } else {
                throw new Exception(LOCATION + "Can't delete folder!");
            }
            // logger.debug( LOCATION + "::END");
        } catch (Exception syse) {
            throw syse;
        } finally {
            // close folder FileObject
            close(fileObj, LOCATION);
        }
    }

    /**
     * 
     */
    public InputStream getInputStreamFileContent(String filePath) throws Exception {
        final String LOCATION = "::getInputStreamFileContent(" + filePath + ")";
        // logger.debug( LOCATION + "::BEGIN");
        try {
            final FileObject fileObj = m_fileMgr.resolveFile(filePath, opts);
            if (!fileObj.exists()) {
                return null;
            }
            // logger.debug( LOCATION + "::END");
            return fileObj.getContent().getInputStream();
        } catch (Exception fe) {
            throw new Exception(LOCATION, fe);
        }
    }

    /**
     * 
     */
    public String getFileContent(String filePath) throws Exception {

        final String LOCATION = "::getFileContent(" + filePath + ")";

        // logger.debug( LOCATION + "::BEGIN");
        InputStream in = null;
        InputStreamReader inTmp = null;
        BufferedReader rdr = null;
        FileObject fileObj = null;
        FileContent fileContent = null;
        try {
            fileObj = m_fileMgr.resolveFile(filePath, opts);
            fileContent = (fileObj.exists() ? fileObj.getContent() : null);
            in = (fileContent == null ? null : fileContent.getInputStream());

            if (in == null) {
                return null;
            }

            int i;
            StringBuffer str = new StringBuffer();
            char[] charTmp = new char[2048];
            inTmp = new InputStreamReader(in, UNICODE_ENCODE);
            rdr = new BufferedReader(inTmp);
            while ((i = rdr.read(charTmp, 0, 2048)) != -1) {
                str.append(new String(charTmp, 0, i));
            }

            // logger.debug( LOCATION + "::END");
            return str.toString();
        } catch (Exception ioe) {
            throw new Exception(LOCATION, ioe);
        } finally {
            close(rdr, LOCATION);
            close(inTmp, LOCATION);
            close(in, LOCATION);
            close(fileContent, LOCATION);
            close(fileObj, LOCATION);
        }
    }

    /**
     * 
     */
    public byte[] getByteFileContent(String filePath) throws Exception {

        final String LOCATION = "::getByteFileContent(" + filePath + ")";

        // logger.debug( LOCATION + "::BEGIN");
        InputStream in = null;
        FileObject fileObj = null;
        FileContent fileContent = null;
        ByteArrayOutputStream bout = null;
        try {
            fileObj = m_fileMgr.resolveFile(filePath, opts);        
//            System.out.println("URL: " + fileObj.getURL().toString());            
            fileContent = (fileObj.exists() ? fileObj.getContent() : null);            
            in = (fileContent == null ? null : fileContent.getInputStream());

            if (in == null) {
                return null;
            }

            int _MB = 2048;

            bout = new ByteArrayOutputStream();
            byte[] b = new byte[_MB];
            int i;
            while ((i = in.read(b, 0, _MB)) != -1) {
                bout.write(b, 0, i);
            }

            // logger.debug( LOCATION + "::END");
            return bout.toByteArray();
        } catch (Exception ioe) {
            throw new Exception(LOCATION, ioe);
        } finally {
            close(in, LOCATION);
            close(fileContent, LOCATION);
            close(fileObj, LOCATION);
            close(bout, LOCATION);
        }
    }

    /**
     * 
     */
    public void createFolder(String folderPath) throws Exception {

        final String LOCATION = "::createFolder(" + folderPath + ")";

        // logger.debug( LOCATION + "::BEGIN");
        FileObject fileObj = null;
        try {
            fileObj = m_fileMgr.resolveFile(folderPath, opts);
            fileObj.createFolder();
            // logger.debug( LOCATION + "::END");
        } catch (Exception fe) {
            throw new Exception(LOCATION, fe);
        } finally {
            close(fileObj, LOCATION);
        }
    }

    /**
     * 
     */
    public boolean isExist(String filePath) throws Exception {

        final String LOCATION = "::isExist(" + filePath + ")";

        // logger.debug( LOCATION + "::BEGIN");
        boolean rs = false;
        FileObject fileObj = null;
        try {
            fileObj = m_fileMgr.resolveFile(filePath, opts);
            // logger.debug( LOCATION + "::END");
            rs = fileObj.exists();
        } catch (Exception e) {
            rs = false;
        } finally {
            close(fileObj, LOCATION);
        }
        return rs;
    }

    /**
     * 
     */
    public boolean isFolder(String filePath) throws Exception {

        final String LOCATION = "::isFolder(" + filePath + ")";

        // logger.debug( LOCATION + "::BEGIN");
        boolean rs = false;
        FileObject fileObj = null;
        try {
            fileObj = m_fileMgr.resolveFile(filePath, opts);
            rs = (fileObj.exists() && fileObj.getType() == FileType.FOLDER);
        } catch (Exception e) {
            rs = false;
        } finally {
            close(fileObj, LOCATION);
        }
        // logger.debug( LOCATION + "::END");
        return rs;
    }

    /*
     * (non-Javadoc)
     * 
     * @see vn.com.vistar.commons.filemngt.FilesAdapter#isFile(java.lang.String)
     */
    public boolean isFile(String filePath) throws Exception {
        final String LOCATION = "::isFile(" + filePath + ")";

        // logger.debug( LOCATION + "::BEGIN");
        boolean rs = false;
        FileObject fileObj = null;
        try {
            fileObj = m_fileMgr.resolveFile(filePath, opts);
            rs = (fileObj.exists() && fileObj.getType() == FileType.FILE);
        } catch (Exception e) {
            rs = false;
        } finally {
            close(fileObj, LOCATION);
        }
        // logger.debug( LOCATION + "::END");
        return rs;
    }

    /**
     * @param fileNames
     * @param folderPath
     * @return
     * @throws Exception
     */
    public String findFirstFileOnFolder(String[] fileNames, String folderPath) throws Exception {

        final String LOCATION = "::findFirstFileOnFolder(fileName=" + fileNames + ", folderPath=" + folderPath + ")";

        // logger.debug( LOCATION + "::BEGIN");
        FileObject fileObj = null;
        FileObject objTmp = null;
        FileObject[] childFiles = null;
        try {
            fileObj = m_fileMgr.resolveFile(folderPath, opts);
            boolean hasChilds = false;
            try {
                hasChilds = (fileObj.exists() && fileObj.getChildren() != null);
            } catch (Exception e) {
                hasChilds = false;
            }

            if (hasChilds) {
                String strListFiles = convertToString(fileNames);
                childFiles = fileObj.getChildren();
                int i, size;

                size = (childFiles == null ? 0 : childFiles.length);
                for (i = 0; i < size; i++) {
                    objTmp = childFiles[i];

                    if (isExistOnListFile(objTmp, strListFiles)) {
                        return objTmp.getName().getBaseName();
                    } else {
                        close(objTmp, LOCATION);
                    }
                }
            }
            // logger.debug( LOCATION + "::END");
            return null;
        } catch (Exception fe) {
            throw new Exception(LOCATION, fe);
        } finally {
            close(fileObj, LOCATION);
            close(childFiles, LOCATION);
        }
    }

    /**
     * @param fileName
     * @param folderPath
     * @return folder contain file that this file exist on listFileNames.
     * @throws Exception
     */
    public String findFirstFolderContainFile(String[] fileNames, String folderPath) throws Exception {

        final String LOCATION = "::findFirstFolderContainFile(fileName=" + fileNames + ", folderPath=" + folderPath + ")";

        // logger.debug( LOCATION + "::BEGIN");
        String str = null;
        FileObject fileObj = null;
        FileObject resultFileObj = null;
        try {
            fileObj = m_fileMgr.resolveFile(folderPath, opts);
            boolean hasChilds = false;
            try {
                hasChilds = (fileObj.exists() && fileObj.getChildren() != null);
            } catch (Exception e) {
                hasChilds = false;
            }

            if (hasChilds) {
                resultFileObj = findFirstFile(convertToString(fileNames), fileObj);
                str = (resultFileObj == null ? null : resultFileObj.getParent().getName().getURI());
            }
            // logger.debug( LOCATION + "::END");
            return str;
        } catch (Exception syse) {
            throw syse;
        } finally {
            close(fileObj, LOCATION);
            close(resultFileObj, LOCATION);
        }
    }

    /**
     * @param listFileName
     * @param folderObj
     * @return
     * @throws Exception
     */
    private FileObject findFirstFile(String listFileName, FileObject folderObj) throws Exception {

        String LOCATION = "findFirstFile(listFileName=" + listFileName + ", folderObj=" + folderObj + ")";
        FileObject returnObj = null;
        FileObject objTmp1 = null;
        FileObject lstObj[] = null;
        Collection<FileObject> lstFolder = new ArrayList<FileObject>();
        try {
            if (listFileName != null && folderObj != null && folderObj.exists() && folderObj.getType() == FileType.FOLDER) {
                lstObj = folderObj.getChildren();
                int i, size;
                size = (lstObj == null ? 0 : lstObj.length);
                for (i = 0; i < size; i++) {
                    objTmp1 = lstObj[i];
                    if (objTmp1.getType() == FileType.FOLDER) {
                        lstFolder.add(objTmp1);
                    } else {
                        if (isExistOnListFile(objTmp1, listFileName)) {
                            returnObj = objTmp1;
                            return returnObj;
                        } else {
                            close(objTmp1, LOCATION);
                        }
                    }
                }
                Iterator<FileObject> iter = lstFolder.iterator();
                while (iter.hasNext()) {
                    objTmp1 = iter.next();
                    returnObj = findFirstFile(listFileName, objTmp1);
                    close(objTmp1, LOCATION);
                    if (returnObj != null) {
                        return returnObj;
                    }
                }
            }
            return returnObj;
        } catch (Exception syse) {
            throw syse;
        } finally {
            close(lstObj, returnObj, LOCATION);
            close(lstFolder, returnObj, LOCATION);
        }
    }

    /**
     * 
     */
    public String getURI(String filePath) throws Exception {

        final String LOCATION = "getURI(fileName=" + filePath + ")";
        if (filePath == null) {
            throw new Exception(LOCATION + "File " + filePath + " was NULL!");
        }

        FileObject fileObj = null;
        try {
            fileObj = m_fileMgr.resolveFile(filePath, opts);
            if (!fileObj.exists()) {
                throw new Exception("File " + filePath + " does not exist!");
            }
            return fileObj.getName().getURI();
        } catch (Exception e) {
            throw new Exception(LOCATION, e);
        } finally {
            close(fileObj, LOCATION);
        }
    }

    /**
     * 
     */
    public URL getURL(String filePath) throws Exception {

        final String LOCATION = "getURI(fileName=" + filePath + ")";
        if (filePath == null) {
            throw new Exception(LOCATION + "File " + filePath + " was NULL!");
        }

        FileObject fileObj = null;
        try {
            fileObj = m_fileMgr.resolveFile(filePath, opts);
            if (!fileObj.exists()) {
                throw new Exception("File " + filePath + " does not exist!");
            }
            return fileObj.getURL();
        } catch (Exception e) {
            throw new Exception(LOCATION, e);
        } finally {
            close(fileObj, LOCATION);
        }
    }

    /**
     * @param strConnection
     * @throws Exception
     */
    public void testConnection(String strConnection) throws Exception {

        final String PARAM = "strConnection=" + strConnection;
        final String LOCATION = "testConnection(" + PARAM + ")";

        // m_logger.logInfo(LOCATION + ":: BEGIN");
        FileObject fileObj = null;
        FileObject fileObjCreate = null;
        try {
            if (strConnection == null || strConnection.trim().length() == 0) {
                throw new Exception(LOCATION + "ConnectionString does not exist ....!!!");
            }

            fileObj = m_fileMgr.resolveFile(strConnection, opts);

            // +++ checking connect to server
            fileObj.exists();

            // m_logger.logInfo(LOCATION + ":: END");
        } catch (Exception fex) {
            throw fex;
        } finally {
            close(fileObj, LOCATION);
            close(fileObjCreate, LOCATION);
        }
    }

    /**
     * Find the file has file name like a cetaint string in a folder.
     * 
     * @param fileName
     * @param folderPath
     * @return the file path to the file. return null if file not found.
     * @throws Exception
     */
    public String findFile(String fileName, String folderPath) throws Exception {

        final String PARAMS = "[ file name = " + fileName + ", folder path = " + folderPath + " ]";
        final String LOCATION = "::findFile(" + PARAMS + ")";
        logger.debug(LOCATION + ":: BEGIN");

        if (fileName == null || fileName.trim().length() == 0 || folderPath == null || folderPath.trim().length() == 0) {
            throw new Exception(LOCATION + "File name or folder path is null or empty");
        }

        String location = null;
        FileObject folder = null;
        FileObject file = null;
        FileObject[] files = null;
        try {

            folder = m_fileMgr.resolveFile(folderPath, opts);
            if (folder != null && folder.exists() && folder.getType().equals(FileType.FOLDER)) {

                // first search for file by get child
                file = folder.getChild(fileName);
                if (file != null && file.exists()) {
                    location = file.getURL().toString();
                } else {
                    // if file not found by get child then iterate through all
                    // files in this folder.
                    files = folder.getChildren();
                    for (int i = 0; i < files.length; i++) {
                        if (FileType.FILE.equals(files[i].getType()) && (files[i].getName().getBaseName().indexOf(fileName) != -1)) {
                            location = files[i].getName().getURI();
                            break;
                        }
                    } // end for
                }
            }
            logger.debug(LOCATION + ":: END - return value: " + location);
            return location;

        } catch (Exception fe) {
            throw new Exception(LOCATION, fe);
        } finally {
            close(folder, LOCATION);
            close(file, LOCATION);
            close(files, LOCATION);
        }
    }

    /**
     * @param file
     * @param excludeFiles
     * @return
     */
    private boolean isExistOnListFile(FileObject file, String strListFiles) {

        boolean rs = false;
        if (strListFiles != null && file != null) {
            String baseName = "\"" + file.getName().getBaseName() + ";";
            rs = (strListFiles.indexOf(baseName) != -1);
        }
        return rs;
    }

    /**
     * @param lstStr
     * @return
     */
    private String convertToString(String[] lstStr) {

        if (lstStr == null)
            return null;
        StringBuffer str = new StringBuffer();
        int i, size = lstStr.length;
        for (i = 0; i < size; i++) {
            str.append("\"").append(lstStr[i]).append(";");
        }
        return str.toString();
    }

    /**
     * @param srcFile
     * @param destFolder
     * @return
     * @throws Exception
     */
    private int check(FileObject srcFile, FileObject destFolder) throws Exception {

        String LOCATION = "::check(srcFile=" + (srcFile == null ? "NULL" : srcFile.getName().getPath()) + ", destFolder=" + (destFolder == null ? "NULL" : destFolder.getName().getPath()) + ")";
        int rs = NO_COPY;

        if (destFolder != null && srcFile != null) {
            FileObject fileTmp = null;
            try {
                // check srcFile was exist & srcFile was file.
                if (srcFile.exists() && srcFile.getType() == FileType.FILE) {
                    if (destFolder.exists() && destFolder.getType() == FileType.FOLDER) {
                        fileTmp = destFolder.getChild(srcFile.getName().getBaseName());

                        if (fileTmp != null && fileTmp.exists() && fileTmp.getType() == FileType.FILE) {
                            if (compare(fileTmp.getContent().getInputStream(), srcFile.getContent().getInputStream())) {
                                rs = NO_COPY;

                            } else {
                                rs = CHANGE;
                            }
                        } else if (fileTmp != null && fileTmp.exists() && fileTmp.getType() == FileType.FOLDER) {
                            rs = CHANGE;
                        } else {
                            rs = COPY;
                        }
                    } else {
                        rs = COPY;
                    }
                }
            } catch (Exception fex) {
                throw new Exception(LOCATION, fex);
            } finally {
                close(fileTmp, LOCATION);
            }
        }
        return rs;
    }

    /**
     * return TRUE if io1 & io2 are the same.
     * 
     * @param io1
     * @param io2
     * @return
     */
    private boolean compare(InputStream io1, InputStream io2) {

        final String LOCATION = "::compare(io1=" + (io1 == null ? "NULL" : "NOT NULL") + ", io2=" + (io2 == null ? "NULL" : "NOT NULL") + ")";
        boolean rs = false;
        if (io1 != null && io2 != null) {
            int c1 = 0;
            int c2 = 0;
            try {
                while (true) {
                    c1 = io1.read();
                    c2 = io2.read();
                    if (c1 != c2 || c1 == -1 || c2 == -1) {
                        break;
                    }
                }
                if (c1 == -1 && c2 == -1) {
                    rs = true;
                }
            } catch (Exception e) {
                rs = false;
            } finally {
                close(io1, LOCATION);
                close(io2, LOCATION);
            }
        }

        return rs;
    }

    /**
     * @param filePath
     * @return
     * @throws Exception
     */
    public String getMimetype(String filePath) throws Exception {

        final String LOCATION = "::getMimetype(" + filePath + ")";

        // logger.debug( LOCATION + "::BEGIN");
        FileObject fileObj = null;
        try {
            fileObj = m_fileMgr.resolveFile(filePath, opts);
            if (fileObj.exists() && fileObj.getType() == FileType.FILE) {
                return fileObj.getContent().getContentInfo().getContentType();
            } else {
                throw new Exception(LOCATION + "Can not get mimetype...");
            }
            // logger.debug( LOCATION + "::END");
        } catch (Exception e) {
            throw new Exception(LOCATION, e);
        } finally {
            close(fileObj, LOCATION);
        }
    }

    /**
     * @param filePath
     * @return
     * @throws Exception
     */
    public String getFileType(String filePath) throws Exception {

        final String LOCATION = "::getFileType(" + filePath + ")";

        // logger.debug( LOCATION + "::BEGIN");

        try {
            int index = (filePath == null ? -1 : filePath.lastIndexOf("."));
            if (index > -1) {
                return (filePath.substring(index + 1));
            } else {
                return null;
            }

            // logger.debug( LOCATION + "::END");
        } catch (Exception e) {
            throw new Exception(LOCATION, e);
        }
    }

    /**
     * @author DucNM
     * @param filePath
     * @return size of File
     */
    public long getFileSize(String filePath) {
        try {
            FileObject fileObj = m_fileMgr.resolveFile(filePath, opts);
            if (fileObj != null) {
                return fileObj.getContent().getSize();
            } else {
                return -1;
            }
        } catch (Exception e) {
            return -1;
        }
    }

    /**
     * 
     * @param filePath
     * @return
     */
    public long getLastModifiedTime(String filePath) {
        try {
            FileObject fileObj = m_fileMgr.resolveFile(filePath, opts);
            if (fileObj != null) {
                return fileObj.getContent().getLastModifiedTime();
            } else {
                return -1;
            }
        } catch (Exception e) {
            return -1;
        }
    }
    
    public static void main(String[] args) {
    }
}
